package server;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.config.ThreadPollConfig;
import server.request.RequestProcessor;
import server.servlet.HttpServlet;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.*;

/**
 * @author cg
 * @date 2023/6/7 13:17
 */
public class BootStrap {
    //定义端口号
    private int port = 8080;

    private final String webConfig = "web.xml";

    private final String parameterConfig = "tomcat.properties";

    private Class<?> config;

    /**
     * 保存url和servlet的请求映射关系
     */
    private static Map<String, HttpServlet> httpServletMap = new HashMap<>();

    private final Object lock = new Object();

    public BootStrap() {
        config = this.getClass();
    }

    public BootStrap(Class<?> config) {
        this.config = config;
    }

    public static void main(String[] args) {
        BootStrap bootStrap = new BootStrap();
        bootStrap.start();
    }

    public void start() {
        try {
            //初始化servlet
            loadServlet();
            //初始化server配置
            loadServer();
//        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 15, 2, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10));
            //初始化线程池
            ThreadPoolExecutor poolExecutor = ThreadPollConfig.getThreadPool(parameterConfig);
            //创建socket监听端口
            ServerSocket serverSocket = new ServerSocket(port);

            System.out.println("服务器启动，绑定" + port + "端口");
            while (true) {
                //监听
                Socket socket = serverSocket.accept();
                synchronized (this.getClass()) {
                    //处理socket
                    RequestProcessor requestProcessor = new RequestProcessor(socket, httpServletMap);
                    poolExecutor.execute(requestProcessor);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 初始化server配置
     */
    private void loadServer() {
        String prefix = "server.";
        Properties properties = new Properties();
        try {
            properties.load(this.getClass().getClassLoader().getResourceAsStream("tomcat.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String port = properties.getProperty(prefix + "port");
        this.port = port == null ? 8080 : Integer.parseInt(port);
    }

    /**
     * 解析xml中的请求路径，并映射到对应的servlet类中
     */
    private void loadServlet() {
        //加载web.xml文件
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(webConfig);
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            //获取根元素
            Element rootElement = document.getRootElement();
            //找到所有servlet标签
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element selectNode : selectNodes) {
                //解析<servlet-name>
                String servletName = selectNode.selectSingleNode("servlet-name").getStringValue();
                //解析<servlet-class>
                String servletClass = selectNode.selectSingleNode("servlet-class").getStringValue();

                //2, 根据servlet-name找到<servlet-mapping>中与其匹配的<url-pattern>
                //Xpath表达式：从/web-app/servlet-mapping下查询，查询出<servlet-name=servletName>的元素
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']'");
                String url = servletMapping.selectSingleNode("url-pattern").getStringValue();
                Class<?> clazz = Class.forName(servletClass);
                Object instance = clazz.newInstance();
                httpServletMap.put(url, (HttpServlet) instance);
                //执行初始化操作
                Method initMethod = clazz.getDeclaredMethod("init", Class.class);
                initMethod.setAccessible(true);
                initMethod.invoke(instance, config);
            }
        } catch (NoSuchMethodException ignored) {
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (inputStream != null) inputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

}
